home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac100% 1998 November
/
MAC100-1998-11.ISO.7z
/
MAC100-1998-11.ISO
/
オンラインソフト定点観測
/
ユーティリティ
/
Mops 3.2.sea
/
Mops 3.2
/
Mops source
/
PPC source
/
cg-regs
< prev
next >
Wrap
Text File
|
1998-05-20
|
48KB
|
2,031 lines
marker m__cg-regs
PPC?
[IF]
false constant debug?
false constant recompTest?
[ELSE]
false constant debug?
false constant recompTest?
[THEN]
(* This file defines the classes we use to describe the PPC registers,
and creates the register objects.
*)
3 constant spill_cnt ¥ the number of regs we spill if we can't
¥ otherwise get a free one
0 value #gprs_cleared ¥ used by the spilling code, to count
¥ the GPRs we actually free up
¥ Some useful boilerplate instructions:
PPC?
[IF] ¥ In this case these are already defined
¥ in the 68k image, and we can't interpret the << ops,
¥ so we'll do it this way:
LR>R0 constant LR>R0 ¥ mflr r0
R0>LR constant R0>LR ¥ mtlr r0
BLR constant BLR ¥ unconditional branch to link reg
[ELSE]
31 26 <<
8 16 << or
339 1 << or constant LR>R0 ¥ mflr r0
31 26 <<
8 16 << or
467 1 << or constant R0>LR ¥ mtlr r0
19 26 <<
$ 14 21 << or
16 1 << or constant BLR ¥ unconditional branch to link reg
[THEN]
: GPR>CTR ( reg# -- ) 21 << $ 7C0903A6 or code, ;
: CTR>GPR ( reg# -- ) 21 << $ 7C0902A6 or code, ;
: nop, ( -- ) $ 7C000378 code, ; ¥ or r0, r0, r0
forward SPILL
forward check_for_moved_stores
:class REFERENCE_LIST super{ reference obj_array }
int SIZE
:m SIZE: inline{ get: size} get: size ;m
:m >SiZE: inline{ put: size} put: size ;m
:m +SIZE: inline{ +: size} +: size ;m
:m STK: { n ¥ index -- }
¥ Using self as a stack, selects the n'th
¥ cell. We don't report an error if n is greater than the
¥ current depth, since there are situations in equalizing over
¥ basic blocks where it would be a big pest to check all the time.
¥ We just make sure such out-of-range cells return "noRef" type.
ASSERT{ n 0> } ¥ error if stk: called with a neg or zero index
get: size n - -> index
index 0<
IF limit 1- select: self
noRef >refType: self
ELSE
index select: self
THEN
;m
:m PUSH: ¥ ( ^ref -- )
get: size select: self
->: self
1 +: size
;m
:m MOVEDOWN: ¥ moves all items "down" to make room for another.
¥ Leaves element zero selected.
get: size
IF get: size
FOR i ^elem
i 1+ select: self
->: self
NEXT
THEN
1 +: size
0 select: self
;m
:m MOVEUP:
get: size NIF ." moveup: finds zero size" cr
printall: self ( 1 die )
THEN
0 select: self free: self ¥ note - reg is now selected
get: refType
SELECT[ gprRef ]=> ?clear_GPR
[ crRef ]=> ?clear_CR
DEFAULT=> drop ¥ not an error - just nothing to do
]SELECT
1 -: size
get: size 0
?DO i 1+ ^elem
i select: self
->: self
LOOP
;m
:m SAVE:
get: size 0
?DO i select: self stack: self LOOP
get: size
;m
:m RESTORE:
dup put: size ?dup 0EXIT
FOR i select: self unstack: self NEXT
;m
:m PRINTALL:
." depth: " get: size . cr
get: size 0<
IF clear: size EXIT THEN
get: size 0EXIT
get: current
get: size
FOR ?pause i select: self print: self NEXT
select: self
;m
;class
24 reference_list CSTK ¥ Compile time stack - maps the run-time
¥ data stack to regs
24 reference_list CSTK2 ¥ Used in equalizing between basic blocks
24 reference_list CSTK2_ORIG ¥ Ditto
24 reference_list CSTK_TEMP ¥ For scratch while equalizing
24 reference_list FCSTK ¥ Floating compile time stack
24 reference_list FCSTK2
24 reference_list FCSTK2_ORIG
24 reference_list FCSTK_TEMP
objPtr aRef class_is reference
objPtr aRef2 class_is reference
objPtr aRefL class_is reference_list
(* ODs_CLASS is an array of OD objects, defined using obj_array.
We'll use this class for our 3 register files - GPRs, FPRs and CRs.
*)
:class ODs_CLASS super{ OD large_obj_array }
objPtr spillODs class_is ODs_class
int last_allocated
int alloc_limit ¥ last reg# we can allocate
:m LAST_ALLOCATED: get: last_allocated ;m
:m >LAST_ALLOCATED: put: last_allocated ;m
:m >ALLOC_LIMIT: put: alloc_limit ;m
private
(* We call is_reg_unused?: in the first loop while trying to find a free
reg. An "unused" reg is preferable to one with a zero refCnt, since
the latter could still hold a valid value that could be reused in
future, or may have just recently been used and so not be avaliable
for retargetting an earlier op.
Factoring out this method allows us to tinker with it a bit.
*)
:m is_reg_unused?: ( -- b )
(* First we can only grab a reg if its refCnt is zero. If it's
nonzero, the reg is live, so we can't use it no matter what.
(This also allows us to block a particular reg being allocated,
even if it's empty, by setting its refcnt nonzero. We need to
do this for CR0 in particular.
*)
get: refCnt IF false EXIT THEN
(* Now we look for a completely unused reg, or one with type
otUnknown and lastRefCDP at or before the current basic block
start. That's just as good, as it could never be reused, and
could never block a retargetting.
*)
get: opType NIF true EXIT THEN
get: opType otUnknownCodes > IF false EXIT THEN
get: lastRefCDP basic_block_start u<=
IF
get: opCDP basic_block_start u>
IF
get: opCDP put: lastRefCDP
THEN
true EXIT
THEN
false
;m
public
(* GetFreeReg: does just that. We first try to find a completely unused
reg. If that fails, we can do one of two things - we can grab a reg
which is inactive (zero refcnt) but with a valid value, or we
can spill some of the stack to memory which will free active
regs. We used to try to keep at least 3 recently computed
values, and so spilled if there were 3 or less inactive regs.
But this was disastrous if we were doing an equalization, and
anyway a spill should probably be a last resort thing anyway.
So now we only spill if we're right out of regs.
Note, that one thing that's tempting to do is call update_refcnts
in case there's a reg that's apparently referenced but really isn't.
This can happen. But it's not safe to call update_refcnts here, since
we can be in the middle of doing just about anything when we need a
free reg. We might have grabbed an operand or two into opnd1, opnd2
or res1, and no longer have a reference in cstk, which would lead
to us grabbing a reg that's in use. We must only call update_refcnts
at places where we know it's safe. So if we end up spilling regs when
one was really free already, that's just bad luck.
*)
:m GETFREEREG: { ¥ found? reg# #inActive earliestInactive inactiveCDP
spilled? -- reg# }
false -> found? false -> spilled?
0 -> #inActive 0 -> earliestInactive -1 -> inactiveCDP
BEGIN ¥ will loop if there are no free regs and we have to spill
¥ first we try to find a completely unused reg:
get: alloc_limit 1+ 0
DO i select: self
is_reg_unused?: self
IF
debug? if
." allocating empty reg " i . cr
then
clear: self allocate: self
i UNLOOP EXIT
THEN
get: refCnt
NIF 1 ++> #inActive
get: lastRefCDP inactiveCDP u<
IF get: lastRefCDP -> inactiveCDP
i -> earliestInactive
THEN
THEN
LOOP
¥ not found yet. We now look for an inactive reg.
#inactive 0>
IF
earliestInactive select: self
debug? if
." allocating inactive reg " print: myRef cr
then
clear: self allocate: self
earliestInactive EXIT
THEN
¥ still none found. We now spill to free up some regs. If we've
¥ already spilled, we've got problems. Hopefully this shouldn't
¥ happen.
spilled? NIF self -> spillODs spill ELSE 211 die THEN
AGAIN
;m
:m ALLOCATE_REG: ¥ ( reg# -- )
select: self allocate: self ;m
:m FREE_REG: ¥ ( reg# -- )
select: self free: self ;m
:m ?DELETE_REG: ¥ ( reg# -- )
select: self ?delete: self ;m
:m MATCH?: { ^OD canBeSpecial? ¥ svCurrent -- b }
get: current -> svCurrent
limit 0
DO i select: self
get: special?
IF canBeSpecial?
ELSE true
THEN
IF ^OD =?: self
IF ¥ equal, but need to check limit on validity
CDP get: validTillCDP u<
IF unloop true
debug? if
." match?: matched on this reg: " print: myRef cr
then
EXIT
THEN
THEN
THEN
LOOP
svCurrent select: self
false
;m
:m clearAll:
limit 0
DO i select: self full_clear: self
-1 put: last_allocated
LOOP
;m
:m clearAllWithBoundary: { bdry -- }
¥ called when we don't want a full clear on the non-volatile regs.
limit 0
DO i select: self
i bdry < IF full_clear: self ELSE clear: self THEN
-1 put: last_allocated
LOOP
;m
:m INVALIDATE_ALL: ¥ when we just need to invalidate, not completely clear
limit 0
DO i select: self clear: opType
LOOP
;m
:m CLEAR_REFCNTS: ¥ called from update_refcnts - see comment there.
get: current
get: alloc_limit 0
DO i select: self clear: refCnt
LOOP
select: self
;m
¥ UPDATE_ALL_REFS: replaces all occurrences of oldRef by newRef in
¥ the OD array. Used when we've moved a register.
:m UPDATE_ALL_REFS: { ^oldRef ^newRef fromCDP -- }
get: current
limit 0
DO i select: self ^oldRef ^newRef fromCDP update_refs: self
LOOP
select: self
;m
¥ REG_CHANGED: looks after the situation where a reg is getting changed,
¥ so for any regs which depend on the changed reg, we need to set its
¥ validTillCDP ivar to the current CDP.
:m REG_CHANGED: { ^ref -- }
get: current
limit 0
DO i select: self ^ref ?antecedent_changed: self
LOOP
select: self
;m
:m INVALIDATE_ON_OVERLAP: { ^OD ¥ svCurrent -- }
get: current -> svCurrent
limit 0
DO i select: self
^OD overlap?: self
IF ¥ overlaps, but need to check limit on validity
CDP get: validTillCDP u<
IF
debug? if
blit: self .h ^OD blit: class_as> OD .h cr
." overlap?: matched on this reg: " print: self cr
." overlapping OD: " print: [ ^OD ] cr
then
CDP 4- put: validTillCDP
otUnknown put: opType
noType put: instrnType
addr: myRef reg_changed: self
THEN
THEN
LOOP
svCurrent select: self
;m
:m UPDATE_opCDPs:
get: current
limit 0
DO i select: self update_opCDP: self
LOOP
select: self
;m
:m MAKE_ALTERED_REGS_UNKNOWN:
get: current
limit 0
DO i select: self make_unknown_if_altered: self
LOOP
select: self
;m
:m MAKE_FETCHES_UNKNOWN:
get: current
limit 0
DO i select: self make_unknown_if_fetch: self
LOOP
select: self
;m
:m ?HOIST_ALL:
get: current
limit 1
DO i select: self ?hoist: self drop
LOOP
select: self
;m
(* MOVEREG: moves an operand from one reg to a different one - we might have
to do this when equalizing the stack, for example. If possible we just
recompile the operation that generated the original result, to generate it
straight in the new reg. If all else fails we'll actually compile an
instruction to move the operand.
We leave the destination register selected.
*)
:m REG_MOVED: { old# recompiled? -- }
¥ housekeeping routine called after a move. The new reg is currently
¥ selected. We update references and clear the old reg.
debug? if
." reg_moved: called - dest:" print: self
then
addr: myRef dup ->: tmpRef1 ->: tmpRef2 old# >reg: tmpRef1
tmpRef1 tmpRef2 get: opCDP 4+
current: self old# select: self
clear: super
recompiled?
IF ref_gone: self
ELSE CDP 4- put: lastRefCDP
THEN
select: self
update_refs
;m
:m MOVEREG_BY_RECOMPILING?: { old# new# ¥ qqq -- recompile? }
old# select: self
addr: self copyOD: theOD ¥ move old operand into theOD for convenience
¥ need ALL ivars unchanged in this move
new# select: self
recompTest? if
." movereg_by_recompiling?" cr
." eq_block_recompiling_move? " eq_block_recompiling_move? . cr
." source reg in theOD:" print: theOD cr
." dest reg: " print: self cr
." backstop_CDP " backstop_CDP .h cr
." basic_block_start: " basic_block_start .h cr
." lastRefCDP in dest " get: lastRefCDP .h cr
then
false
¥ Now we decide if we can handle a move by just recompiling the op. There are
¥ several things to check. Note we don't check fetch_backstop, since when we
¥ recompile an op we don't move it, so we can assume any fetches are valid
¥ in their existing location. fetch_backstop only limits where we can
¥ move NEW fetches back to.
recompTest?
if
[ ppc? ] [if] dbgr [then]
then
move_by_recompiling? 0EXIT
¥ for debugging or whatever, we can turn this
¥ optimization off
eq_block_recompiling_move? ?EXIT
¥ if back equalizing, we're doing low-level things
¥ with regs and mustn't try to change anything.
¥ get: ivar> opType in theOD otStore =
¥ [ ppc? not ] [if] dup if db then [then] ?EXIT ¥ shouldn't happen!!
¥ ¥ stores can't be recompiled, since we've wiped out
¥ ¥ the info on what we're storing.
get: ivar> special? in theOD ?EXIT
¥ can't recompile if the old reg is a local or
¥ base reg or whatever (which can't move)
get: ivar> opType in theOD otUnknownCodes <= ?EXIT
¥ or if the old reg is empty or of unknown type
¥ (i.e. nothing to recompile)
get: ivar> opCDP in theOD basic_block_start u< ?EXIT
¥ or if its op wasn't in the current basic blk
get: ivar> opCDP in theOD backstop_CDP u< ?EXIT
¥ or if it would be past the backstop
¥ get: ivar> opType in theOD otFetch =
¥ IF get: ivar> opCDP in theOD fetch_backstop u< ?EXIT THEN
¥ ¥ or the fetch backstop, if it's a fetch
get: lastRefCDP get: ivar> opCDP in theOD u> ?EXIT
¥ or if the last ref to the NEW reg was after the op
¥ we want to recompile, since we'd clobber that use.
¥ note we use >, not >=, since it's OK if the instrn
¥ we're recompiling uses its own reg as an operand.
¥ if we got here, it's OK to recompile the op!
drop ¥ drop false flag
recompTest? if
." moving by recompiling " old# . ." to " new# . cr
then
theOD copyWithCDP: self
get: ivar> refcnt in theOD put: refcnt
recompile: self
old# new# addr: myRef check_for_moved_stores
old# true reg_moved: self
true
;m
:m MOVEREG: { old# new# updateRefs? ¥ extraRefs? -- }
debug? recompTest? or if
." moveReg: called, to move " old# . ." to " new# . cr
then
new# select: self
old# new# = ?EXIT ¥ just in case
¥ now we need to check if we have any extra refs to this dest reg
¥ on cstk. If we do, these refs need the old value, so we'll have
¥ to save the old value to a new reg before we change the dest.
¥ Note that we mustn't do this during equalization when we presumably
¥ have everything under control and mustn't try to second-guess.
equalizing?
NIF
false -> extraRefs?
size: cstk 1+ 1
?DO i stk: cstk
addr: myRef =?: cstk
IF
debug? if
." we have another ref to the dest reg, in cell " i . cr
printall: cstk
then
extraRefs?
NIF ¥ first time - get the new reg
true -> extraRefs?
getFreeReg: self drop
new# compile_reg_move: self
THEN
addr: myRef ->: cstk
THEN
LOOP
THEN
old# new# moveReg_by_recompiling?: self ?EXIT
new# select: self
old# compile_reg_move: self
debug? recompTest? or if
." moved by compiling a move, from " old# . ." to " new# . cr
then
old# -1 addr: myRef check_for_moved_stores
updateRefs?
IF old# false reg_moved: self
new# select: self
THEN
;m
:m indexedOpCDP:
current: self >r
select: self
get: permanent? IF 16 ELSE get: opCDP THEN
r> select: self
;m
:m USE_THIS: ( CDP_to_use reg# -- )
current: self >r
select: self mark_use: super
r> select: self
;m
:m PRINT:
cr
." current: " get: current .
print: super
;m
:m PRINTALL:
cr
." current: " get: current dup . cr
." last allocated: " print: last_allocated cr
limit 0
DO i select: self
get: opType
IF
i . print: super cr
THEN
LOOP
select: self
;m
:m .ALLOCATED:
get: current
limit 0
DO i select: self get: refCnt
IF i . 4 spaces ." refCnt " get: refCnt . cr
THEN
LOOP cr
select: self
;m
:m .FREE:
cr
get: current
limit 0
DO i select: self get: refCnt
NIF i . cr
THEN
LOOP
select: self
;m
:m INIT: { myRefType my_alloc_limit -- }
limit 0
DO i select: self
myRefType >refType: myRef i >reg: myRef
LOOP
my_alloc_limit put: alloc_limit
;m
:m CLASSINIT:
clearall: self
;m
PPC?
[IF] ¥ may not really need this to be conditional, but I'm
¥ cautious...
:m DEEP_CLASSINIT: { ¥ xx -- } ¥ Need this for setting up when we initialize ofter
¥ target compilation, since the regular CLASSINIT:
¥ doesn't get done. Need to override, or it will
¥ just call the method in the first superclass OD.
idxBase 4+ addr: xdispl displ!
^base classinit: class_as> OD ¥ actually can omit once it's working
¥ since ivSetup calls classinit: on
¥ ALL superclasses.
(^base) -> newObject
['] ODs_class ( dup -> xx ) ifa displace 0 0
ivSetup
;m
[THEN]
;class
32 ODs_class GPRs PPC? not [IF] gprRef 10 init: GPRs [THEN]
32 ODs_class FPRs PPC? not [IF] fprRef 13 init: FPRs [THEN]
8 ODs_class CRs PPC? not [IF] CRref 7 init: CRs [THEN]
32 ODs_class STORED_GPRs PPC? not [IF] gprRef 10 init: stored_GPRs [THEN]
32 ODs_class STORED_FPRs PPC? not [IF] fprRef 10 init: stored_FPRs [THEN]
¥ Note: when target compiling we can't send messages at compile time, so we
¥ can't send init:. So we do it at SETUP_CG in cg7. The code there should
¥ agree with the above.
objPtr theRegs class_is ODs_class ¥ Used to point to the appropriate bank
¥ of regs in code which can apply to
¥ more than one
¥ Now we need to permanently allocate regs which we can't use for
¥ general operands:
: ALLOCATE_RESERVED_REGS
current: GPRs current: FPRs
0 allocate_reg: GPRs special: GPRs
rX_reg allocate_reg: GPRs special: GPRs
rY_reg allocate_reg: GPRs special: GPRs
rZ_reg allocate_reg: GPRs special: GPRs
SP_reg allocate_reg: GPRs special: GPRs
FSP_reg allocate_reg: GPRs special: GPRs
SP_reg sys_SP_reg <>
IF
sys_SP_reg allocate_reg: GPRs special: GPRs
THEN
RTOC_reg allocate_reg: GPRs permanent: GPRs
mainData_reg allocate_reg: GPRs permanent: GPRs
modData_reg allocate_reg: GPRs permanent: GPRs
mainCode_reg allocate_reg: GPRs permanent: GPRs
modCode_reg allocate_reg: GPRs permanent: GPRs
RP_reg allocate_reg: GPRs special: GPRs
obj_base_reg allocate_reg: GPRs permanent: GPRs
32 1st_gpr_local
DO i select: GPRs 1 put: ivar> refCnt in GPRs special: GPRs
LOOP
¥ now the FPRs
0 allocate_reg: FPRs special: FPRs
32 1st_fpr_local
DO i select: FPRs 1 put: ivar> refCnt in FPRs special: FPRs
LOOP
select: FPRs select: GPRs
;
PPC? not [IF] allocate_reserved_regs [THEN]
¥ We use these objects to keep track of the operands and results of the
¥ operation we're currently compiling:
reference OPND1
reference OPND2
reference OPND3
reference OPND4
reference RES1
reference RES2
reference RES3
reference TMPREF
0 value EXIT_CHAIN
:f ALLOCATE_GPR allocate_reg: GPRs ;f
:f ALLOCATE_FPR allocate_reg: FPRs ;f
:f ALLOCATE_CR allocate_reg: CRs ;f
:f FREE_GPR free_reg: GPRs ;f
:f FREE_FPR free_reg: FPRs ;f
:f FREE_CR free_reg: CRs ;f
:f DEL_GPR ?delete_reg: GPRs ;f
:f DEL_FPR ?delete_reg: FPRs ;f
:f DEL_CR ?delete_reg: CRs ;f
:f ?CLEAR_GPR get: ivar> refCnt in GPRs
NIF clear: ivar> opType in GPRs 1 ++> #gprs_cleared THEN
;f
:f ?CLEAR_FPR get: ivar> refCnt in FPRs
NIF clear: ivar> opType in FPRs THEN
;f
:f ?CLEAR_CR get: ivar> refCnt in CRs
NIF clear: ivar> opType in CRs THEN
;f
:f USE_GPR use_this: GPRs ;f
:f USE_FPR use_this: FPRs ;f
:f USE_CR use_this: CRs ;f
:f SET_CR0 0 select: CRs
put: ivar> opCDP in CRs
put: ivar> opType in CRs ;f
:f GPR_CDP indexedOpCDP: GPRs ;f
:f FPR_CDP indexedOpCDP: FPRs ;f
:f CR_CDP indexedOpCDP: CRs ;f
(* UPDATE_REFCNTS checks cstk and ensures that the refcnt fields in all regs
are correct. Basic block boundaries or updating refs may get things out
of kilter, so this ensures everything's back to what it should be.
*)
: UPDATE_REFCNTS
clear_refCnts: GPRs
clear_refCnts: FPRs
clear_refCnts: CRs
¥ don't worry about refCnts in stored_GPRs
size: cstk 1+ 1
?DO i stk: cstk allocate: cstk
LOOP
size: fcstk 1+ 1
?DO i stk: fcstk allocate: fcstk
LOOP
allocate_reserved_regs
;
: MAKE_ALTERED_REGS_UNKNOWN
[ debug? ] [if]
." make_altered_regs_unknown called" cr
[then]
make_altered_regs_unknown: GPRs
make_altered_regs_unknown: FPRs
make_altered_regs_unknown: CRs
make_altered_regs_unknown: stored_GPRs
;
objPtr match_regs class_is ODs_class
objPtr stored_regs class_is ODs_class
: match_stores? { ^regs ^stored_regs ^OD store-code canBeSpecial?
¥ sv_opType -- ^OD' true | -- false }
^regs -> match_regs ^stored_regs -> stored_regs
debug? if
." match? didn't match fetch on regs" cr
." - now attempting to match with stored regs:" cr
print: [ ^OD ]
cr ." stored regs: " cr
printall: stored_regs cr
then
^OD get: ivar> opType in class_as> OD -> sv_opType
store-code ^OD put: ivar> opType in class_as> OD
^OD canBeSpecial? match?: stored_regs
sv_opType ^OD put: ivar> opType in class_as> OD ¥ restore it
debug? if
dup if ." matched on stored regs" else ." didn't match stored regs" then cr cr
then
NIF false EXIT THEN
¥ we've matched on a stored reg. We use it, but change its type
¥ in GPRs/FPRs to otUnknown so we can't change it again. Any attempt
¥ to recompile it, say, would clobber the store (the voice of
¥ experience).
current: stored_regs select: match_regs
addr: stored_regs copyOD: match_regs
otUnknown put: ivar> opType in match_regs
debug? if
." changed type in match_regs to otUnknown:"
print: match_regs
then
CDP put: ivar> lastRefCDP in match_regs
big# put: ivar> validTillCDP in match_regs
addr: match_regs true
;
: MATCH? { ^OD canBeSpecial? ¥ opType -- ^OD' true | -- false }
allow_match? NIF false EXIT THEN
^OD get: ivar> opType in OD -> opType
opType otFPstart otFPend within? nip
IF ¥ it's an FP op - just check FPRs
^OD canBeSpecial? match?: FPRs
IF addr: FPRs true ELSE false THEN EXIT
THEN
^OD canBeSpecial? match?: GPRs IF addr: GPRs true EXIT THEN
^OD canBeSpecial? match?: CRs IF addr: CRs true EXIT THEN
¥ now if the op is a fetch, we need to check for a match on the stores of
¥ that kind of register.
opType otFetch =
IF GPRs stored_GPRs ^OD otStore canBeSpecial? match_stores? EXIT THEN
opType otFPfetch =
IF FPRs stored_FPRs ^OD otFPstore canBeSpecial? match_stores? EXIT THEN
false
;
objPtr rcRef class_is reference
:f REG_CHANGED { ^ref -- }
^ref reg_changed: GPRs
^ref reg_changed: FPRs
^ref reg_changed: CRs
¥ Now if this is a GPR, we also clobber the corresponding element in
¥ stored_GPRs, since any value that was stored isn't in this GPR any more.
^ref -> rcRef
refType: rcRef GPRref =
IF reg: rcRef select: stored_GPRs
clear: ivar> opType in stored_GPRs
THEN
;f
: UPDATE_EQ_RANGES
reset: eq_ranges
BEGIN
len: eq_ranges 0EXIT
nxtL: eq_ranges startCDP u>
UNTIL
-4 skip: eq_ranges
BEGIN
1stL: eq_ranges
deltaCDP + >nxtL: eq_ranges
len: eq_ranges
dup 0< if . ." auugggh!" QUIT then
NUNTIL
;
:f UPDATE_CDPs ¥ ( startCDP deltaCDP -- )
-> deltaCDP -> startCDP
update_opCDPs: GPRs
update_opCDPs: FPRs
update_opCDPs: CRs
update_opCDPs: stored_GPRs
basic_block_start startCDP u>
IF deltaCDP ++> basic_block_start THEN
loop_start startCDP u>
IF deltaCDP ++> loop_start THEN
update: control_stk
update_eq_ranges
fix_containing_loop
;f
objPtr MS_check_regs class_is ODs_class
:f check_for_moved_stores { old# new# ^ref -- }
^ref refType: class_as> reference
SELECT[ GPRref ]=> stored_GPRs -> MS_check_regs
[ FPRref ]=> stored_FPRs -> MS_check_regs
DEFAULT=> drop EXIT
]SELECT
old# select: MS_check_regs
get: ivar> opType in MS_check_regs otStore =
IF
new# 0>=
IF
debug? if
cr
." moving a store since source reg has moved from " old# . ." to " new# . cr
then
addr: MS_check_regs
new# select: MS_check_regs
copyWithCDP: MS_check_regs
recompile: MS_check_regs
old# select: MS_check_regs
THEN
clear: ivar> opType in MS_check_regs
THEN
;f
false value USING_CR0
(* MOVE_CR_BIT moves a bit in the CR from one position to another.
Note that we can't do this by recompiling the op, since the op
was a compare or an arith instruction that necessarily put the
bit where it ended up (except for the one case where it was a
test for the SAME condition which happened to be sent to a different
CR field).
The move can be done in one instruction - either a cror or crnor
depending on whether the 1_is_true? bit is the same or different.
*)
: MOVE_CR_BIT { srcRef dstRef ¥ whichSrcBit whichDstBit -- }
debug? if
." move_cr_bit called with: " print: [ srcRef ] print: [ dstRef ] cr
then
false -> check_OP_stores? ¥ classes mightn't match (might be cstk)
¥ but doesn't matter here
srcRef -> aRef
dstRef -> aRef2
true -> check_OP_stores?
clear: instrn
19 >primOp: instrn
1_is_true?: aRef 1_is_true?: aRef2 =
IF 449 ¥ cror
ELSE 33 ¥ crnor
THEN >secOp: instrn
reg: aRef 4* bit#: aRef or -> whichSrcBit
reg: aRef2 4* bit#: aRef2 or -> whichDstBit
whichSrcBit dup >rA: instrn >rB: instrn
whichDstBit >rD: instrn
compile: instrn
;
¥ ===============================================
¥ More utility words
: STK ¥ Selects the nth cstk cell (1 is top)
stk: cstk ;
: FSTK
stk: fcstk ;
: POP { ^ref -- }
size: cstk
IF 1 stk cstk ^ref ->: class_as> reference
-1 +size: cstk
ELSE ¥ no operands in regs - we just have to adjust stk_offset
1cell ++> stk_offset
noRef ^ref >refType: class_as> reference
THEN
;
: FPOP { ^ref -- }
size: fcstk
IF 1 fstk fcstk ^ref ->: class_as> reference
-1 +size: fcstk
ELSE ¥ no operands in regs - we just have to adjust stk_offset
fpcell ++> fstk_offset
noRef ^ref >refType: class_as> reference
THEN
;
: PUSH ¥ ( ^ref -- )
push: cstk ;
: FPUSH ¥ ( ^ref -- )
push: fcstk ;
: INIT_CSTK
0 >size: cstk ;
: INIT_FCSTK
0 >size: fcstk ;
: INIT_GPRs
debug? if
." init_gprs called - clearing everything" cr
then
clearAll: GPRs
clearAll: CRs
clearAll: stored_GPRs
allocate_reserved_regs
;
: INIT_FPRs
debug? if
." init_fprs called - clearing everything" cr
then
clearAll: FPRs
clearAll: stored_FPRs
allocate_reserved_regs
;
: INIT_VOLATILE_GPRs
debug? if
." init_volatile_gprs called" cr
then
13 clearAllWithBoundary: GPRs
clearAll: CRs
clearAll: stored_GPRs
allocate_reserved_regs
;
: INIT_VOLATILE_FPRs
debug? if
." init_volatile_gprs called" cr
then
14 clearAllWithBoundary: FPRs
clearAll: stored_FPRs
allocate_reserved_regs
;
: set_backstop_CDP
CDP -> backstop_CDP ( init_volatile_regs )
;
: (SETUP_CSTK) { #gprs init? -- }
init? IF init_gprs ELSE init_volatile_gprs THEN
init_cstk
#gprs 0
?DO i 3+ dup allocate_reg: GPRs >GPR: res1
otUnknown put: ivar> opType in GPRs
noType put: ivar> instrnType in GPRs
res1 push
LOOP
;
: (SETUP_FCSTK) { #fprs init? -- }
init? IF init_fprs ELSE init_volatile_fprs THEN
init_fcstk
#fprs 0
?DO i 1+ dup allocate_reg: FPRs >FPR: res1
otUnknown put: ivar> opType in FPRs
noType put: ivar> instrnType in FPRs
res1 fpush
LOOP
;
: SETUP_CSTK ( #gprs -- )
true (setup_cstk) ;
: RESET_CSTK ( #gprs -- )
false (setup_cstk) ;
: SETUP_FCSTK ( #fprs -- )
true (setup_fcstk) ;
: RESET_FCSTK ( #fprs -- )
false (setup_fcstk) ;
:f UPDATE_REFS { ^oldRef ^newRef fromCDP -- } ¥ this isn't a big bottleneck
^oldRef ^newRef fromCDP update_all_refs: GPRs
^oldRef ^newRef fromCDP update_all_refs: FPRs
^oldRef ^newRef fromCDP update_all_refs: CRs
^oldRef ^newRef fromCDP update_all_refs: stored_GPRs
size: cstk 0EXIT
current: cstk
size: cstk FOR
i select: cstk
^oldRef =?: cstk IF ^newRef ->: cstk THEN
NEXT
update_refCnts
select: cstk
;f
: OPERANDS { n ¥ #toPull siz -- }
(* Ensures we have the top n stk cells in regs for a subsequent
operation. Pops n operands off cstk, and moves them to opnd1, opnd2
etc., with opnd1 being the LOWEST stack cell.
We could also free the regs, which would be safe if
we allocate the the result reg(s) first. But I'd have to check
if the reference is actually a reg, and this has to be done anyway
when I compile the op. So it might be easier to free the reg
there, not here.
*)
size: cstk -> siz
n siz > ¥ do we need to pull cells out of memory?
IF n size: cstk - -> #toPull
#toPull
FOR getFreeReg: GPRs >gpr: res1
SP_reg stk_offset 0 compPull: GPRs
1cell ++> stk_offset
movedown: cstk res1 ->: cstk
NEXT
THEN
n
SELECT[ 1 ]=> 1 stk cstk ->: opnd1
-1 +size: cstk
[ 2 ]=> 2 stk cstk ->: opnd1
1 stk cstk ->: opnd2
-2 +size: cstk
[ 3 ]=> 3 stk cstk ->: opnd1
2 stk cstk ->: opnd2
1 stk cstk ->: opnd3
-3 +size: cstk
[ 4 ]=> 4 stk cstk ->: opnd1
3 stk cstk ->: opnd2
2 stk cstk ->: opnd3
1 stk cstk ->: opnd4
-4 +size: cstk
DEFAULT=> ." illegal parameter to OPERANDS : " . 1 die
]SELECT
;
: FOPERANDS { n ¥ #toPull siz -- }
size: fcstk -> siz
n siz > ¥ do we need to pull cells out of memory?
IF n size: fcstk - -> #toPull
#toPull
FOR getFreeReg: FPRs >fpr: res1
FSP_reg fstk_offset 0 compPull: FPRs
FPcell ++> fstk_offset
movedown: fcstk res1 ->: fcstk
NEXT
THEN
n
SELECT[ 1 ]=> 1 fstk fcstk ->: opnd1
-1 +size: fcstk
[ 2 ]=> 2 fstk fcstk ->: opnd1
1 fstk fcstk ->: opnd2
-2 +size: fcstk
[ 3 ]=> 3 fstk fcstk ->: opnd1
2 fstk fcstk ->: opnd2
1 fstk fcstk ->: opnd3
-3 +size: fcstk
[ 4 ]=> 4 fstk fcstk ->: opnd1
3 fstk fcstk ->: opnd2
2 fstk fcstk ->: opnd3
1 fstk fcstk ->: opnd4
-4 +size: fcstk
DEFAULT=> ." illegal parameter to OPERANDS : " . 1 die
]SELECT
;
: RESULTS ¥ ( n -- ) Reserves n GPRs for results
SELECT[ 1 ]=> getFreeReg: GPRs >gpr: res1
[ 2 ]=> getFreeReg: GPRs >gpr: res1
getFreeReg: GPRs >gpr: res2
DEFAULT=> ." illegal parameter to RESULTS : " . 1 die
]SELECT
;
: FRESULTS ¥ ( n -- ) Reserves n FPRs for results
SELECT[ 1 ]=> getFreeReg: FPRs >fpr: res1
[ 2 ]=> getFreeReg: FPRs >fpr: res1
getFreeReg: FPRs >fpr: res2
DEFAULT=> ." illegal parameter to RESULTS : " . 1 die
]SELECT
;
: SWAP_CSTK
2 operands
opnd2 push opnd1 push ;
: ROT_CSTK
3 operands
opnd2 push opnd3 push opnd1 push ;
(* CR_RESULT reserves a CR field. If it's for a comparison result,
the actual condition must be in subOperation. The result is
left in res1, and the allocated CR reg is left selected.
If it's just to get a free CR reg for a CR logical operation,
don't bother setting subOperation, and ignore res1.
If we want a particular CR (which may be CR0 for an integer op or CR1
for an FP op, we pass true for wantOne? as well as the reg# we want
and the CDP where the op is to be compiled. If we don't want a
particular one, we pass false and the other two parameters are ignored.
*)
: CR_RESULT { wantOne? CR#_wanted CDP_where_used ¥ gotit? -- }
false -> gotit?
wantOne?
IF CR#_wanted select: CRs
get: ivar> refCnt in CRs
NIF get: ivar> opCDP in CRs CDP_where_used u<= -> gotit?
THEN
gotit?
IF allocate: CRs CR#_wanted THEN
THEN
gotit?
NIF
0 select: CRs allocate: CRs ¥ temporarily, to ensure they won't be free
1 select: CRs allocate: CRs
getFreeReg: CRs
0 select: CRs free: CRs
1 select: CRs free: CRs
THEN
( CR# we got )
dup >CR: res1 select: CRs
subOperation >condition: res1
res1 ->: ivar> myRef in CRs
;
0 value svSelector
0 value svOpcode
objPtr matchedOD class_is OD
: MATCH&ALLOCATE? { canBeSpecial? -- b }
theOD canBeSpecial? match? NIF false EXIT THEN
false -> check_OP_stores?
-> matchedOD
true -> check_OP_stores?
debug? if
." match&allocate? matched on reg: " print: matchedOD cr
then
allocate: matchedOD addr: ivar> myRef in matchedOD ->: res1
¥ if it's a CR result, although we've matched on the CR field, the
¥ condition might be different. So we make sure we set the right
¥ condition in res1 and the matching CR reg. The condition should
¥ be in subOperation.
refType: res1 crRef =
IF subOperation >condition: res1
res1 ->: ivar> myRef in CRs
THEN
true
;
¥ LIT>GPR compiles the passed in literal value in a gpr, and leaves
¥ res1 set to that gpr. Uses theOD.
¥ Note: This is only called from equalization, where we mustn't ever ever
¥ generate duplicate references (since we're getting rid of them!),
¥ so we don't look for a match.
: LIT>GPR { n canBeSpecial? -- }
n setLit: theOD
debug? if
." lit>gpr - theOD:" print: theOD
then
¥ canBeSpecial? match&allocate? ?EXIT - aauugghhh!!
getFreeReg: GPRs >gpr: res1
theOD ->: GPRs compile: GPRs
;
: LIT>SELECTED_GPR { n -- }
n setLit: GPRs
compile: GPRs
;
: LIT>THIS_GPR { n gpr# -- }
gpr# select: GPRs
n setLit: GPRs compile: GPRs
;
0 value #CRs_pushed
0 value #FPRs_pushed
(* CR>THIS_GPR compiles the sequence to convert a CR bit reference to
a true or false in the GPR whose number is passed in.
This stuff looks incredibly complicated, but that's because we try
to generate the optimized sequences given in the Compiler Writers'
Guide, whenever we can, and there are a lot of special cases.
One way to do the job would be to put a -1 into the reg, then
conditionally branch over a clear of the reg. But we should always try
to eliminate branches. The most general way is to move the CR to a reg,
then rotate-left-and-mask to get the desired bit into the low bit position
of the reg. Then unlike C, we need to add a negate or a subtact 1, so that
we get a proper true flag. We handle this general case in do_cr_op below.
But in most cases we can do better. The Guide says that CR ops can
cause a stall since they operate on the whole CR, and so clobber any
parallelism involving different CR fields. So if we can, we avoid
using a CR op. Now if the CR result is a comparison (which it usually is),
then we can change the op to a subfc or something similar, then do 2 or 3
instructions of bit twiddling to get a flag without any CR ops or branches.
The code sequences are very obscure, involving some unobvious uses of the
carry flag.
All this is further complicated by the fact that we can't really compile
arbitrary code here since
1. Routines like compRegReg aren't re-entrant, and
assume theOD stays valid.
2. We might be called from within equalization which means we should
leave other regs alone.
3. We can't use r12 (rY) since we might be in the middle of setting
up a method call. We can use r10 and r11 (rX and rZ).
4. If we have to do the subtract 1, we can't be in r0 (addi doesn't work
(on r0).
So we basically use rX, rZ and r0 where we can, target the destination
gpr with the final instruction, hand-wind things and leave everything
else alone.
We also free the CR here since that usually simplifies things for the
caller, and we're definitely finished with the CR once we've moved its
value to a GPR.
*)
: make_flag { reg1 reg2 gpr# 1_is_true? otCode -- }
1_is_true?
IF gpr# ELSE rZ_reg THEN select: GPRs
reg2 >Agpr: GPRs reg1 >Bgpr: GPRs
otCode put: ivar> opType in GPRs
compile: GPRs
1_is_true?
NIF
gpr# select: GPRs
rZ_reg >Agpr: GPRs clear: ivar> B_opnd in GPRs
otNOT put: ivar> opType in GPRs
compile: GPRs
THEN
set: ivar> dontHoist? in GPRs ¥ it depends on hand-wound preceding
¥ code, so mustn't move
;
: make_flag_for_zcomp { reg gpr# litval otCode subcode -- }
gpr# select: GPRs
otCode put: ivar> opType in GPRs
subcode put: ivar> subtype in GPRs ¥ right arithmetic shift
reg >Agpr: GPRs
litval >Blit: GPRs
compile: GPRs
set: ivar> dontHoist? in GPRs
;
: do_signed_comp_with_zero { reg gpr# rev? 1_is_true? -- }
¥ reg >Agpr: GPRs
rev?
IF 1_is_true?
IF ¥ 0>
otSubfc put: ivar> opType in GPRs
0 >Blit: GPRs
compile: GPRs
¥ 0 gpr# 31 otShift 3 make_flag_for_zcomp
$ 540A0FFE
reg 21 << or code, ¥ rlwinm rZ, reg, 1, 31, 31
rZ_reg dup gpr# true otAddme make_flag
ELSE ¥ 0<=
otAddc put: ivar> opType in GPRs
-1 >Blit: GPRs
compile: GPRs
¥ 0 gpr# 31 otShift 3 make_flag_for_zcomp
¥
¥ otAddic put: ivar> opType in GPRs
¥ -1 >Blit: GPRs
¥ compile: GPRs
¥
$ 540A0FFE
reg 21 << or code, ¥ rlwinm rZ, reg, 1, 31, 31
rZ_reg dup gpr# true otSubfze make_flag
THEN
ELSE
1_is_true?
IF ¥ 0<
reg gpr# 31 otShift 3 make_flag_for_zcomp
ELSE ¥ 0>=
$ 540A0FFE
reg 21 << or code, ¥ rlwinm rZ, reg, 1, 31, 31
rZ_reg gpr# -1 otAdd 0 make_flag_for_zcomp
THEN
THEN
;
: do_signed_lit_op { reg gpr# litval rev? 1_is_true? -- }
0 select: GPRs
reg >Agpr: GPRs
¥ addr: CRs copyWithCDP: GPRs
¥ delete: CRs ¥ we'll instead be doing an op into r0
clear: ivar> subtype in GPRs ¥ we always want this
litval
NIF reg gpr# rev? 1_is_true? do_signed_comp_with_zero EXIT THEN
1_is_true?
NIF
rev? IF 1 ++> litval ELSE 1 --> litval THEN
not> rev?
THEN
rev?
NIF rX_reg rZ_reg litval negate otAddc
ELSE rZ_reg rX_reg litval otSubfc
THEN
put: ivar> opType in GPRs
>Blit: GPRs
compile: GPRs
( rZ/rX ) 21 <<
$ 39400000 or
litval 31 >> or code, ¥ li rZ/rX, 1/0
( rX/rZ ) 16 <<
$ 54000FFE or
reg 21 << or code, ¥ rlwinm rX/rZ, reg, 1, 31, 31
rX_reg rZ_reg gpr# true otSubfe make_flag
;
: do_signed_op { reg1 reg2 gpr# litval 1_is_true? -- }
reg1 0< IF reg2 gpr# litval false 1_is_true? do_signed_lit_op EXIT THEN
reg2 0< IF reg1 gpr# litval true 1_is_true? do_signed_lit_op EXIT THEN
1_is_true?
IF $ 540A0FFE
reg1 21 << or code, ¥ rlwinm rZ, reg1, 1, 31, 31
$ 540B0FFE
reg2 21 << or code, ¥ rlwinm rX, reg2, 1, 31, 31
otSubfc
ELSE
$ 6C0A8000
reg1 21 << or code, ¥ xoris rZ, reg1, $ 8000
otSub
THEN
0 select: GPRs
¥ addr: CRs copyWithCDP: GPRs
¥ delete: CRs ¥ we'll instead be doing some kind of subtract into r0
( code ) put: ivar> opType in GPRs clear: ivar> subtype in GPRs
reg2 >Agpr: GPRs reg1 >Bgpr: GPRs
compile: GPRs
1_is_true?
IF
rX_reg rZ_reg
ELSE
$ 7C005014 code, ¥ addc r0, r0, rZ
0 0
THEN
gpr# true otSubfe make_flag
;
: do_unsigned_op { reg1 reg2 gpr# litval 1_is_true? -- }
rZ_reg select: GPRs
¥ addr: CRs copyWithCDP: GPRs
¥ delete: CRs ¥ we'll instead be doing a subfc into rZ
reg2 0<
IF
litval >Blit: GPRs
reg1 otSubfc
ELSE
reg1 0<
IF litval negate >Blit: GPRs
reg2 otAddc
ELSE
reg1 >Bgpr: GPRs
reg2 otSubfc
THEN
THEN
put: ivar> opType in GPRs >Agpr: GPRs
clear: ivar> subtype in GPRs
compile: GPRs ¥ subfc rZ, reg1, reg2 or whatever
rZ_reg dup gpr# 1_is_true? otSubfe make_flag
;
: do_zero_test { reg1 gpr# 1_is_true? -- }
reg1 >Agpr: GPRs
1_is_true?
IF
-1 >Blit: GPRs
otAddc put: ivar> opType in GPRs
ELSE
0 >Blit: GPRs
otSubfc put: ivar> opType in GPRs
THEN
compile: GPRs
rZ_reg dup gpr# true otSubfe make_flag
¥ flag will be already the right way around
¥ so we pass true, not 1_in_true? - and we
¥ won't need the adjustment instruction.
;
: do_equality { reg1 reg2 gpr# litval 1_is_true? -- }
rZ_reg select: GPRs
¥ addr: CRs copyWithCDP: GPRs ¥ we'll be replacing the compare with an xor
¥ into rZ, and not use the CR field at all
otXOR put: ivar> opType in GPRs clear: ivar> subtype in GPRs
reg1 >Agpr: GPRs
reg2 0<
IF ¥ it's literal - and if zero, we can do even better, by
¥ deleting the CR op, omitting the xor entirely, and
¥ skipping straight to our final zero test.
litval
NIF
¥ delete: CRs
reg1 gpr# 1_is_true? do_zero_test EXIT
THEN
litval >Blit: GPRs
ELSE
reg2 >Bgpr: GPRs
THEN
¥ recompile: GPRs ¥ xor rZ, reg1, reg2 / xori rZ, reg1, litval
compile: GPRs
rZ_reg gpr# 1_is_true? do_zero_test
¥ update_refcnts
debug? if
." cr>this_gpr used do_equality, leaving result in:" print: GPRs cr dasm .al
then
;
: do_cr_op { gpr# field# bit# 1_is_true? -- }
rX_reg select: GPRs
$ 7C000026 rX_reg 21 << or code, ¥ mfcr rX
¥ We now get the bit we want into the low bit posn of rX.
otShift&mask put: ivar> opType in GPRs
rX_reg >Agpr: GPRs
field# 4* bit# + 1+ >Blit: GPRs ¥ rotate by one more than the bit #
¥ to get it into the low bit posn
31 put: ivar> maskBegin in GPRs
31 put: ivar> maskEnd in GPRs
compile: GPRs
¥ now we have to do a negate or subtract 1. We now target the requested gpr#,
¥ and leave it selected at the end. There's no problem if this is r0.
gpr# select: GPRs
rX_reg >Agpr: GPRs
1_is_true?
IF ¥ we need to do a negate.
otNeg put: ivar> opType in GPRs
noRef >Btype: GPRs
ELSE ¥ we need to do an addi -1.
otAdd put: ivar> opType in GPRs
-1 >Blit: GPRs
THEN
compile: GPRs
debug? if
." cr>this_gpr used do_cr_op, leaving result in:" print: GPRs cr dasm
then
;
: CR>THIS_GPR { ^ref gpr# ¥ field# bit# 1_is_true? reg1 reg2 litval op opt? -- }
debug? if
cr ." cr>this_gpr called with:" cr
print: [ ^ref ]
." to go to gpr" gpr# . cr
printall: cstk
then
-1 -> litval false -> opt?
¥ now, what's the bit in rX that we want?
^ref get: ivar> field# in class_as> reference -> field#
^ref get: ivar> bit# in class_as> reference -> bit#
^ref get: ivar> 1_is_true? in class_as> reference -> 1_is_true?
field# select: CRs
free: CRs ¥ we always want it freed, and it's safe to do
¥ it now
¥ we don't try to do a better optimization if the op isn't a
¥ compare.
get: ivar> opType in CRs -> op
op otUCMP = op otCMP = or
IF true -> opt?
¥ which optimized sequence we use depends on what the op is, and the
¥ exact condition we're testing for.
Areg: CRs -> reg1
Btype: CRs litRef =
IF Blit: CRs -> litVal
-1 -> reg2
litval $ ffff8000 = IF false -> opt? THEN
¥ if optimizing we sometimes negate the literal - if it's the max
¥ neg 16-bit number this won't work, so as this is very unusual
¥ we'll just not do the optimization in this case.
op otUCMP = IF litval NIF false -> opt? THEN THEN
¥ likewise if the op is unsigned and the lit is zero, the algorithm
¥ won't work properly. But again this is a rather bizarre case so
¥ we'll just avoid it.
ELSE
Breg: CRs -> reg2
THEN
THEN
opt?
NIF ¥ we have to do it the conservative way
gpr# field# bit# 1_is_true? do_cr_op EXIT
THEN
?delete: CRs ¥ delete the compare if it's safe to do so
bit# 2 = IF reg1 reg2 gpr# litval 1_is_true? do_equality EXIT THEN
reg1 reg2 bit# NIF swap THEN
op otCMP =
IF
gpr# litval 1_is_true? do_signed_op
ELSE
gpr# litval 1_is_true? do_unsigned_op
THEN
¥ update_refcnts
debug? if
." cr>this_gpr finished, leaving result in:" print: GPRs cr dasm .al
then
;
¥ CR>GPR is similar, but grabs a free GPR to use, and leaves its reference
¥ in res1. Frees the CR field.
: CR>GPR ( ^ref -- )
getFreeReg: GPRs dup >gpr: res1
cr>this_gpr
;
¥ __>g can be used in an inline defn to force a comparison result into
¥ a GPR, for those situations where we know this will give better
¥ code.
: __>g
1 operands
opnd1 push
reftype: opnd1 crRef <> ?EXIT ¥ do nothing if we don't have a
¥ CR reference
1 operands
opnd1 cr>gpr res1 push
; immediate
: PUSH_TO_MEM { ^ref stkReg stkOffs update? ¥ refType -- }
^ref refType: class_as> reference
SELECT[ gprRef ]=> ^ref reg: class_as> reference select: GPRs
stkReg stkOffs update? compPush: GPRs
[ fprRef ]=> ^ref reg: class_as> reference select: FPRs
stkReg stkOffs update? compPush: FPRs
[ CRref ]=> ¥ we have to convert to a flag, since once
¥ a cell is pushed to mem we don't know what
¥ it is any more.
^ref 0 cr>this_gpr ¥ leaves r0 selected
stkReg stkOffs update? compPush: GPRs
[ litRef ]=> ¥ we have to get the lit to a GPR then push it.
¥ We might be doing a spill, so we won't allocate
¥ a free GPR (there mightn't be one), but just
¥ use r0.
^ref lit: class_as> reference
0 select: GPRs lit>selected_gpr
stkReg stkOffs update? compPush: GPRs
DEFAULT=> drop
]SELECT
;
: PUSH&MOVEUP
0 select: cstk
refType: cstk FPRref =
IF
8 --> fstk_offset
cstk FSP_reg fstk_offset false push_to_mem
ELSE
1cell --> stk_offset
cstk SP_reg stk_offset false push_to_mem
THEN
moveUp: cstk
;
:f SPILL
debug? if
." spilling to get a free reg" cr printall: cstk .al
.gs
¥ [ ppc? not ] [if] zs [then]
then
spillODs FPRs = IF ." FPR spill!!" cr [ ppc? ] [if] dbgr [then] then
0 -> #gprs_cleared
BEGIN
size: cstk 0= if
printall: cstk .al .gs cr
dasm
1 die
then
push&moveup
#gprs_cleared spill_cnt >=
size: cstk 0= or
UNTIL
debug? if
." after spill:" cr printall: cstk
then
;f
: GET_TO_REG? { ^ref ¥ changed? -- changed? }
false -> check_OP_stores?
^ref -> aRef ¥ may be a reference_list, not a reference, but OK
true -> check_OP_stores?
false -> changed?
refType: aRef
SELECT[ litRef ]=> lit: aRef true lit>gpr res1 ->: aRef
true -> changed?
[ gprRef ]=>
[ fprRef ]=>
[ crRef ]=>
DEFAULT=> drop
]SELECT
changed?
;
: GET_TO_GPR? { ^ref ¥ changed? -- changed? }
false -> check_OP_stores?
^ref -> aRef ¥ may be a reference_list, not a reference, but OK
true -> check_OP_stores?
false -> changed?
refType: aRef
SELECT[ gprRef ]=> ¥ nothing to do!
[ fprRef ]=> to_be_written
[ CRref ]=> aRef cr>gpr
res1 ->: aRef true -> changed?
[ litRef ]=> lit: aRef true lit>gpr
res1 ->: aRef true -> changed?
DEFAULT=> drop
]SELECT
changed?
debug? if
." get_to_gpr? leaves result in: " print: res1 cr
then
;
: GET_TO_THIS_GPR { ^ref reg# -- }
false -> check_OP_stores?
^ref -> aRef ¥ may be a reference_list, not a reference, but OK
true -> check_OP_stores?
refType: aRef
SELECT[ gprRef ]=> reg: aRef reg# true moveReg: GPRs
[ fprRef ]=> to_be_written
[ CRref ]=> aRef reg# cr>this_gpr
[ litRef ]=> lit: aRef reg# lit>this_gpr
DEFAULT=> drop
]SELECT
reg# >gpr: res1 res1 ->: aRef
debug? if
." get_to_this_gpr leaves result in: " print: res1 cr
then
;
:f .G select: GPRs print: GPRs ;f
:f .F select: FPRs print: FPRs ;f
:f .C select: CRs print: CRs ;f
:f .GS printall: GPRs ;f
:f .CS printall: CRs ;f
:f .AL ." GPRs:" cr .allocated: GPRs ." CRs" cr .allocated: CRs
." FPRs:" cr .allocated: FPRs cr ;f
:f .FR .free: GPRs ;f
:f .FAL ." FPRs:" cr .allocated: FPRs ;f
: .g3
3 select: GPRs print: GPRs
;
: .cstk
printall: cstk ;
: .cstk2
printall: cstk2 ;
: .cflgs
printall: control_flags ;
endload
¥ =========== the current test block ============
+echox
int ii
:f TEST { ¥ x -- }
cr cr ." hi there one and all!" cr 1 2 3
begin
query cr
begin
rest nip 0>
while
defined?
if execute
else
number
setup_cg
.al
get: ivar> opType in GPRs otNOT = .
then
repeat
.s cr
again
;f
:f quit test ;f ¥ temp so we can catch errors!
endload